#ifndef ACPI_H
#define ACPI_H

static const char * header_signatures[25] = 
{
    "APIC",
    "DSDT",
    "ECDT",
    "FACP",
    "FACS",
    "OEM",
    "PSDT",
    "RSDT",
    "SBST",
    "SLIT",
    "SRAT",
    "SSDT",
    "XSDT",
    "BOOT",
    "CPEP",
    "DBGP",
    "ETDT",
    "HPET",
    "MCFG",
    "SPCR",
    "SPMI",
    "TCPA",
    "WDAT",
    "WDRT",
    "WSPT"
};

static const char * pm_profiles[8] = 
{
    "Unspecified",
    "Desktop",
    "Mobile",
    "Workstation",
    "Enterprise Server",
    "SOHO Server",
    "Appliance PC",
    "Performance Servcer"
};

/* ACPI APIC sub-structures */
struct acpi_apic_lapic
{
    uint8_t  type;
    uint8_t  length;
    uint8_t  cpu_id;
    uint8_t  apic_id;
    uint32_t flags;
} acpi_apic_lapic[64];

struct acpi_apic_ioapic
{
    uint8_t  type;
    uint8_t  length;
    uint8_t  apic_id;
    uint8_t  reserved;
    uint32_t ioapic_address;
    uint32_t interrupt_base_address;
} acpi_apic_ioapic[8];

struct acpi_apic_iso
{
    uint8_t  type;
    uint8_t  length;
    uint8_t  bus;
    uint8_t  source;
    uint32_t global_system_interrupt;
    uint16_t flags;
} acpi_apic_iso[16];

struct acpi_apic_nmi
{
    uint8_t  type;
    uint8_t  length;
    uint16_t flags;
    uint32_t global_system_interrupt;
} acpi_apic_nmi[16];

struct acpi_apic_lapic_nmi
{
    uint8_t  type;
    uint8_t  length;
    uint8_t  apic_id;
    uint16_t flags;
    uint8_t  apic_lint;
} acpi_apic_lapic_nmi[16];

struct acpi_apic_lapic_ao
{
    uint8_t  type;
    uint8_t  length;
    uint16_t reserved;
    uint32_t lapic_address[2];
} acpi_apic_lapic_ao[16];

struct acpi_apic_iosapic
{
    uint8_t  type;
    uint8_t  length;
    uint8_t  apic_id;
    uint8_t  reserved;
    uint32_t global_system_interrupt;
    uint32_t io_sapic_address[2];
} acpi_apic_iosapic[16];

/* ACPI APIC structure */
struct acpi_apic
{
    uint8_t  signature[4];
    uint32_t length;
    uint8_t  revision;
    uint8_t  checksum;
    uint8_t  oemid[6];
    uint8_t  table_oemid[8];
    uint32_t oem_revision;
    uint8_t  creator_id[4];
    uint32_t creator_revision;
    
    uint32_t lapic_address;
    uint32_t flags;
} acpi_apic;

/* ... this is a whopper ... */
/* ACPI FACP structure */
struct acpi_facp
{
    uint8_t  signature[4];
    uint32_t length;
    uint8_t  revision;
    uint8_t  checksum;
    uint8_t  oem_id[6];
    uint8_t  table_oemid[8];
    uint32_t oem_revision;
    uint8_t  creator_id[4];
    uint32_t creator_revision;
    
    uint32_t firmware_ctrl;
    uint32_t dsdt_address;
    uint8_t  reserved0;
    uint8_t  preferred_pm_profile;
    uint16_t sci_int;
    uint32_t smi_cmd;
    uint8_t  acpi_enable;
    uint8_t  acpi_disable;
    uint8_t  s4bios_req;
    uint8_t  pstate_cnt;
    uint32_t pm1a_evt_blk;
    uint32_t pm1b_evt_blk;
    uint32_t pm1a_cnt_blk;
    uint32_t pm1b_cnt_blk;
    uint32_t pm2_cnt_blk;
    uint32_t pm_tmr_blk;
    uint32_t gpe0_blk;
    uint32_t gpe1_blk;
    uint8_t  pm1_evt_len;
    uint8_t  pm1_cnt_len;
    uint8_t  pm2_cnt_len;
    uint8_t  pm_tmr_len;
    uint8_t  gpe0_blk_len;
    uint8_t  gpe1_blk_len;
    uint8_t  gpe1_base;
    uint8_t  cst_cnt;
    uint16_t p_lvl2_lat;
    uint16_t p_lvl3_lat;
    uint16_t flush_size;
    uint16_t flush_stride;
    uint8_t  duty_offset;
    uint8_t  duty_width;
    uint8_t  day_alarm;
    uint8_t  mon_alarm;
    uint8_t  century;
    uint16_t iapc_boot_arch;
    uint8_t  reserved1;
    uint32_t flags;
    uint8_t  reset_reg[12];
    uint8_t  reset_value;
    uint8_t  reserved2[3];
} acpi_facp;

/* ACPI RSDT structure */
struct acpi_rsdt
{
    uint8_t  signature[4];
    uint32_t length;
    uint8_t  revision;
    uint8_t  checksum;
    uint8_t  oem_id[6];
    uint8_t  oem_table_id[8];
    uint32_t oem_revision;
    uint8_t  creator_id[4];
    uint32_t creator_revision;
} acpi_rsdt;

/* ACPI RSDP structure */
struct acpi_rsdp
{
    uint8_t  signature[8];
    uint8_t  checksum;
    uint8_t  oem_id[6];
    uint8_t  revision;
    uint32_t rsdt_address;
    uint32_t length;
} acpi_rsdp;

uint32_t acpi_lapic_count,
         acpi_ioapic_count,
         acpi_iso_count,
         acpi_nmi_count,
         acpi_lapic_nmi_count,
         acpi_lapic_ao_count,
         acpi_iosapic_count;

uint8_t * acpi_facp_oem_id;

bool acpi_present;
bool acpi_parsing_completed;

void parse_acpi(void);

#endif

